<?php
/**
 * Created by PhpStorm.
 * User: lin.zhonghuang
 * Date: 2017/9/12
 * Time: 17:48
 */

namespace inframe\library;

use inframe\Component;
use inframe\throws\library\mongo\DatabaseNotSelectException;
use MongoClient;
use MongoDB;
use MongoCollection;
use MongoCursor;
use MongoException;
use MongoCursorException;
use inframe\throws\library\mongo\MongoException as MException;

/**
 * Class MongoDB
 *
 * php5 和 php7 使用不同的接口
 *
 * @package inframe\library
 */
class Mongo extends Component
{

    const ITERATOR_BREAK = 0; # 中断遍历
    const ITERATOR_CONTINUE = 1;  # 继续遍历


    protected $_config = [
        'host' => '127.0.0.1',
        'port' => 27017,
        'default' => 'inframe', # 默认数据库
    ];
    /**
     * @var string
     */
    protected $currentDatabaseName;
    /**
     * @var MongoDB
     */
    protected $currentDatabase;
    /**
     * @var string
     */
    protected $currentCollectionName = '';
    /**
     * @var MongoCollection
     */
    protected $currentCollection = null;

    public function __construct(array $params = [], $name = 0)
    {
        parent::__construct($params, $name);
        //TODO:自动判别php版本
        $this->_adapter = new MongoClient("mongodb://{$this->_config['host']}:{$this->_config['port']}");
        $this->database($this->currentDatabaseName = $this->_config['default']);
    }

    /**
     * 选择数据库
     * @param string|null $dbname
     * @return $this
     */
    public function database($dbname = null)
    {
        $dbname and $this->currentDatabaseName = $dbname;
        $this->currentDatabase = $this->_adapter->{$this->currentDatabaseName};
        $this->currentCollectionName = '';
        $this->currentCollection = null;
        return $this;
    }

    /**
     * 选择集合
     * @param string $collectName
     * @return $this
     */
    public function collection($collectName)
    {
        $this->currentCollection = $this->currentDatabase->createCollection($this->currentCollectionName = $collectName);
        return $this;
    }

    /**
     * 遍历查找
     * @param callable $iterator
     * @return void
     * @throws DatabaseNotSelectException
     */
    public function iterate(callable $iterator)
    {
        if (!isset($this->currentCollection)) {
            throw new DatabaseNotSelectException();
        }
        /** @var MongoCursor $cursor */
        $cursor = $this->currentCollection->find();
        foreach ($cursor as $document) {
            $res = call_user_func_array($iterator, [$document]);
            if (self::ITERATOR_BREAK === $res) {
                break;
            } elseif (self::ITERATOR_CONTINUE) {
                continue;
            }
        }
    }

    /**
     * 插入数据
     * 发送到数据库的所有字符串必须是 UTF-8 的。如果有字符串不是 UTF-8，将会抛出 MongoException 异常。 要插入（或者查询）一个非 UTF-8 的字符串，请使用 MongoBinData。
     *
     * 【X】(不成立) 调用 MongoCollection::insert 时设置了 w 后，插入两个具有相同 _id 的元素时，导致抛出 MongoCursorException 的例子
     * @param array $data
     * @_param bool $unique 是否要求数据唯一
     * @throws DatabaseNotSelectException
     * @throws MException
     */
    public function insert(array $data)
    {
        if (!isset($this->currentCollection)) {
            throw new DatabaseNotSelectException();
        }
        try {
            $res = $this->currentCollection->insert($data, [
                'w' => 1,# write concern
            ]);
            if ($res['err'] !== null) {
                throw new MException($res['errmsg']);
            }
        } catch (MongoCursorException $exception) {
            throw new MException($exception->getMessage());
        } catch (MongoException $exception) {
            throw new MException($exception->getMessage());
        }
    }

    /**
     * 更新结果：
     * array (
     *  'n' => 1,
     *  'nModified' => 1, # 未修改时为0
     *  'ok' => 1,
     *  'err' => NULL,
     *  'errmsg' => NULL,
     *  'updatedExisting' => true,
     * )
     * @param array $fields 设置字段列表
     * @param array $where where条件
     * @param bool $justone 只更新一条数据
     * @return bool
     * @throws DatabaseNotSelectException
     * @throws MException
     */
    public function update(array $fields, array $where,$justone=true)
    {
        if (!isset($this->currentCollection)) {
            throw new DatabaseNotSelectException();
        }
        $res = $this->currentCollection->update($where, ['$set' => $fields], ['w' => 1,'multiple'=>!$justone]);
        if(isset($res['errmsg'])){
            throw new MException($res['errmsg']);
        }
        return $res['nModified'] > 0;
    }


}